package com.link.gateway.core.cllog;

import com.link.gateway.base.user.model.User;
import com.link.gateway.core.comproperty.RunProperties;
import com.link.gateway.core.kafka.KafkaTemplateFacotry;
import com.link.gateway.core.kafka.conf.KafkaEnv;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Enumeration;
import java.util.concurrent.ExecutionException;

/**
 * 自定义日志打印类
 * @author zhenghongda
 * @date 2019/12/10 17:21
 */
public class LCLogger {

    /**
     * 日志信息有多个部分组成，SEPARATOR分割不同信息， kafka消费者通过此分隔符，将消息拆分，然后存储在es中
     */
    private static final Logger logger = LogManager.getLogger(LCLogger.class);
    private static final String SEPARATOR = "^";
    private static final SimpleDateFormat DF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
    private static final String IP = getIp();


    /**
     * 获取本机的内网ip地址
     * @author gsan
     * @return
     * @throws SocketException
     */
    protected static String getIp()  {
        String localIp = null;// 本地IP，如果没有配置外网IP则返回它
        String netIp = null;// 外网IP
        Enumeration<NetworkInterface> netInterfaces;
        try {
            netInterfaces = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
            withException(e);
            return null;
        }
        InetAddress ip = null;
        boolean finded = false;// 是否找到外网IP
        while (netInterfaces.hasMoreElements() && !finded) {
            NetworkInterface ni = netInterfaces.nextElement();
            Enumeration<InetAddress> address = ni.getInetAddresses();
            while (address.hasMoreElements()) {
                ip = address.nextElement();
                if (!ip.isSiteLocalAddress()
                        && !ip.isLoopbackAddress()
                        && ip.getHostAddress().indexOf(":") == -1) { // 外网IP
                    netIp = ip.getHostAddress();
                    finded = true;
                    break;
                } else if (ip.isSiteLocalAddress()
                        && !ip.isLoopbackAddress()
                        && ip.getHostAddress().indexOf(":") == -1) { // 内网IP
                    localIp = ip.getHostAddress();
                }
            }
        }
        if (netIp != null && !"".equals(netIp)) {
            return netIp;
        } else {
            return localIp;
        }
    }

    /**
     * SimpleDateFormat线程不安全，加锁
     */
    private static String formatDate(Long currentTimeMillis) {
        synchronized (DF) {
            return DF.format(currentTimeMillis);
        }
    }

    /**
     * 返回日志信息构建类，日志级别为info
     * @author 郭闪
     * @date 2019/12/26
     * @param
     *
     */
    public static LogMessageBuilder info() {
        return new LogMessageBuilder("info", getPreClassName(), Thread.currentThread().getId());
    }

    /**
     * 返回日志信息构建类，日志级别为warn
     * @author 郭闪
     * @date 2019/12/26
     * @param
     *
     */
    public static LogMessageBuilder warn() {
        return new LogMessageBuilder("warn", getPreClassName(), Thread.currentThread().getId());
    }

    /**
     * 返回日志信息构建类，日志级别为error
     * @author 郭闪
     * @date 2019/12/26
     * @param
     *
     */
    public static LogMessageBuilder error() {
        return new LogMessageBuilder("error", getPreClassName(), Thread.currentThread().getId());
    }

    /**
     * 获取发生异常的类名称
     * @author 郭闪
     * @date 2019/12/26
     *
     */
    private static String getPreClassName() {
        String className = "";
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        int length = 2;
        if (stackTrace != null && stackTrace.length > length) {
            className = stackTrace[3].getClassName();
        }
        return className;
    }

    /**
     * 打印异常信息并发送至kafka
     * @author 郑宏达
     * @date 2019/12/10 17:27
     */
    public static void withException(Throwable throwable) {

        if (null != throwable.getStackTrace()) {
            String stack = throwable.toString();
            int len = throwable.getStackTrace().length;
            StringBuilder traces = new StringBuilder();
            for (int i = 0; i < len; i++) {
                StackTraceElement element = throwable.getStackTrace()[i];
                if (i == 0) {
                    traces.append(element);
                } else {
                    traces.append("\n").append(element);
                }
            }
            //将日志信息输出到日志文件中
            logger.error("\n\t stack: \t" + stack + "\n\t traces: \t" + traces);
            sendInfo(KafkaTemplateFacotry.LOG_TOPIC, stack, traces.toString());
        }
    }

    /**
     * 打印异常信息并发送至kafka
     * @author 郑宏达
     * @date 2019/12/10 17:27
     * @param message 异常信息
     */
    public static void withException(String message) {
        //将日志信息输出到日志文件中
        logger.error("\n\t 错误日志: \t" + message);
        sendInfo(KafkaTemplateFacotry.LOG_TOPIC, message, "");

    }

    /**
     * 将日志信息发送至kafka
     * @author 郑宏达
     * @date 2019/12/10
     * @param topic kafka topic 名称
     * @param stack 异常信息栈
     * @param traces 异常详细信息
     */
    private static void sendInfo(String topic, String stack, String traces) {
        try {
            KafkaTemplateFacotry.send(topic, exceptionMessageStream(stack, traces));
        } catch (ExecutionException e) {
            logger.error("发送日志失败" + e.getMessage());
        }
    }

    /**
     * 处理异常信息，将其拼接为下面格式：时间^主机Ip^exception^线程ID^--^--^--^Stack^Traces^激活的配置文件
     * @author 郑宏达
     * @date 2019/12/10
     * @param stack 异常信息栈
     * @param traces 异常详细信息
     */
    private static String exceptionMessageStream(String stack, String traces) {
        String userIdAndName = getUserIdAndName();
        // 时间^主机Ip^exception^线程ID^--^--^--^Stack^Traces^激活的配置文件
        return formatDate(System.currentTimeMillis()) + SEPARATOR
                + IP + SEPARATOR
                + "exception" + SEPARATOR
                + Thread.currentThread().getId() + SEPARATOR
                + LogMessageBuilder.handleNull(getPreClassName()) + SEPARATOR
                + "--" + SEPARATOR
                + "--" + SEPARATOR
                + stack + SEPARATOR
                + traces + SEPARATOR
                + LogMessageBuilder.handleNull(KafkaEnv.getProfile()) + SEPARATOR
                + userIdAndName;

    }

    /**
     * 获取用户ID和用户名称，并用^连接
     * @author 郭闪
     * @date 2019/12/10 17:17
     */
    protected static String getUserIdAndName() {
        Long userId = null;
        String userName = null;
        try {
            User user =  RunProperties.TEMP_USER.get();
            if (user != null ) {
                userId =  user.getId();
                userName = user.getUsername();
            }
        } catch (Exception e) {
            //有部分接口确实不需要登录，所以不做处理。
        }
        return  userId + SEPARATOR + userName;
    }
}
